/*   The MIT License
 *   
 *   Carina Engine
 *   Copyright (c) 2013 Zdravko Velinov
 *   
 *   Permission is hereby granted, free of charge, to any person obtaining a copy
 *   of this software and associated documentation files (the "Software"), to deal
 *   in the Software without restriction, including without limitation the rights
 *   to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 *   copies of the Software, and to permit persons to whom the Software is
 *   furnished to do so, subject to the following conditions:
 *
 *   The above copyright notice and this permission notice shall be included in
 *   all copies or substantial portions of the Software.
 *
 *   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 *   IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 *   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 *   AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 *   LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 *   OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 *   THE SOFTWARE.
 */

#ifndef _CARINA_ENGINE_ASSERT_HH_
#define _CARINA_ENGINE_ASSERT_HH_

#include "carina/common/global.hh"
#include "carina/common/patterns.hh"

#include <iostream>
#include <sstream>

#ifdef _MSC_VER
#   include <intrin.h>
#   define CE_TRAP() __debugbreak()
#else
#   include <signal.h>
#   define CE_TRAP() __builtin_trap()
#endif

#ifndef NDEBUG
//! The actual implementation of CE_ASSERT.
#   define _CE_ASSERT(statement, doc_msg, ignore_var) \
         do { \
            static bool ignore_var = false; \
            if(!(statement) && !ignore_var) \
                ignore_var = Carina::Assert(__FILE__ ":" TO_STRING(__LINE__) ": " TO_STRING(statement), doc_msg); \
         } while(0)
/*! \brief The recommended way to place assertions. It includes additional information over the basic standard function.
 *  \param statement    the asserted condition.
 *  \param doc_msg      documentation string that would potentially help the developer that is going to debug it in the future and determine why this should not be the case.
 */
#   define CE_ASSERT(statement, doc_msg) _CE_ASSERT(statement, doc_msg, CONCAT_MACRO(__ignoreAssert, __COUNTER__))
#else
#   define CE_ASSERT(statement, doc_msg)
#endif

//! Dialog message associated with crashed state of the application.
#define CE_CRASH(doc_msg) CrashMessageBox("Application Crashed", doc_msg)

namespace Carina
{
//! Dialog choices that the user can make when faced with error messages generated by the engine.
enum DialogAnswer
{
    CE_ANSWER_ABORT, //!< Abandon the whole execution. However, it breaks the application for convenience during debugging.
    CE_ANSWER_RETRY, /*!< Try to run it, regardless. The whole code must be robust enough to sustain harmless programming mistakes,
                      *   but they must be reported. */
    CE_ANSWER_IGNORE //!< Ignore all errors of this type.
};

/*! \brief Used internally for displaying message boxes about failed assertions under all supported platforms.
 * 
 *  Internally the implementation may vary. Under some platforms that don't have their own standard GUI toolkit Qt or
 *  just a simple command-line message is used. 
 * 
 *  \param title    the message shown in the title bar of the dialog. It should be short and informative.
 *  \param doc_msg  a short description of the circumstances which caused this unexpected behavior.
 *  \returns The answer given by the user/developer.
 */
DialogAnswer AssertMessageBox(const string& title, const string& doc_msg);

/*! \brief Used internally for displaying message boxes about application crashes under all supported platforms.
 * 
 *  Internally the implementation may vary. Under some platforms that don't have their own standard GUI toolkit Qt or
 *  just a simple command-line message is used. 
 * 
 *  \param title    the message shown in the title bar of the dialog. It should be short and informative.
 *  \param doc_msg  a short description of the circumstances which caused this unexpected behavior.
 *  \returns The answer given by the user/developer.
 */
void CrashMessageBox(const string& title, const string& doc_msg);

/*! \brief Returns a backtrace for debugging purposes.
 * 
 *  This function makes formats everything for you so that you can write it to log file, dialog or the standard output.
 *  Don't parse the contents. None of them are standard in any sense. They use the native API. Use other function instead
 *  or create your own. Usually, you want to attach backtraces to crash reports or some serious warning about hard to
 *  track bug.
 *  
 *  \returns A list of the functions that have led to this call.
 */
string Backtrace(size_t start_frame = 1, size_t end_frame = 11);

/*! \brief Intermediate platform-independent assertion.
 *  
 *  \param statement the actual statement which caused the failure.
 *  \param doc_msg   a short description why this statement is unexpected, what could happen and whether there is another way to resolve this issue.
 */
inline bool Assert(const char* statement, const string& doc_msg)
{
    std::stringstream ss;
    ss << statement << "\n\n"
       << doc_msg << "\n\n"
       << Backtrace();
    
    DialogAnswer res = AssertMessageBox("Assertion Failed", ss.str());
    switch(res)
    {
    case CE_ANSWER_ABORT: CE_TRAP(); return false;
    case CE_ANSWER_RETRY: return false;
    case CE_ANSWER_IGNORE: return true;
    }
    
    return false;
}


}

#endif // _CARINA_ENGINE_ASSERT_HH_
